use bc_base::{PrivateKeyBytes, PublicKeyBytes, PublicKeyHashBytes, TransactionValue};
use bc_lib::mining::MiningCommand;
use bc_manager::BcManagerHandler;

// When line is 'exit' return value is true
pub async fn process_input_listener(bc_manager: BcManagerHandler, line: &str) -> bool {
    match parse_command(line) {
        Some(Command::Exit) => true,
        Some(Command::EmptyLine) => false,
        Some(Command::DumpAcc) => {
            handle_dump_acc(&bc_manager).await;
            false
        }
        Some(Command::AddKeyPair(pubkey, private_key)) => {
            handle_add_keypair(&bc_manager, pubkey, private_key).await;
            false
        }
        Some(Command::ReceiveTxTriggerMining(trigger)) => {
            handle_receive_tx_trigger_mining(&bc_manager, trigger).await;
            false
        }
        Some(Command::StartMiningImmediately) => {
            handle_start_mining_immediately(&bc_manager).await;
            false
        }
        Some(Command::ClearUtxos) => {
            handle_clear_utxos(&bc_manager).await;
            false
        }
        Some(Command::TestSendTx) => {
            handle_test_send_tx(&bc_manager).await;
            false
        }
        Some(Command::Help) => {
            print_help();
            false
        }
        None => {
            println!("Unknown command: {}", line);
            print_help();
            false
        }
    }
}

enum Command {
    Exit,
    DumpAcc,
    AddKeyPair(String, String),
    ClearUtxos,
    TestSendTx,
    ReceiveTxTriggerMining(bool),
    StartMiningImmediately,
    Help,
    EmptyLine,
}

fn parse_command(line: &str) -> Option<Command> {
    let tokens: Vec<&str> = line.split_whitespace().collect();
    if tokens.is_empty() {
        return Some(Command::EmptyLine);
    }

    match tokens[0].to_lowercase().as_str() {
        "exit" => Some(Command::Exit),
        "dump-acc" => Some(Command::DumpAcc),
        "add-keypair" => {
            if tokens.len() != 3 {
                return None;
            }
            Some(Command::AddKeyPair(
                tokens[1].to_string(),
                tokens[2].to_string(),
            ))
        }
        "recv-tx-trigger-mining" => {
            if tokens.len() != 2 {
                return None;
            }
            let t = tokens[1] == "true";
            Some(Command::ReceiveTxTriggerMining(t))
        }
        "start-mining-imm" => Some(Command::StartMiningImmediately),
        "clear-utxos" => Some(Command::ClearUtxos),
        "test-send-tx" => Some(Command::TestSendTx),
        "help" => Some(Command::Help),
        _ => None,
    }
}

async fn handle_start_mining_immediately(bc_manager: &BcManagerHandler) {
    bc_manager
        .lock()
        .await
        .send_cmd_to_ming_task(MiningCommand::StartMiningImmediately)
        .await;
    println!("StartMiningImmediately");
}
async fn handle_receive_tx_trigger_mining(bc_manager: &BcManagerHandler, trigger: bool) {
    bc_manager
        .lock()
        .await
        .send_cmd_to_ming_task(MiningCommand::GenBlockWhenReceiveTx(trigger))
        .await;
    println!("Receive tx trigger mining task: {}", trigger);
}

async fn handle_dump_acc(bc_manager: &BcManagerHandler) {
    let mng = bc_manager.lock().await;
    mng.dump_wallet_account().await;
    let item_num_in_wallet = mng.wallet_item_number().await;
    println!("[item_num_in_wallet]:\n{:?}", item_num_in_wallet);
}

async fn handle_add_keypair(bc_manager: &BcManagerHandler, pubkey: String, private_key: String) {
    let pubkey_bytes = match PublicKeyBytes::from_hex_str(&pubkey) {
        Ok(pubkey_bytes) => pubkey_bytes,
        Err(e) => {
            println!("Invalid public key({}), error({})", pubkey, e);
            return;
        }
    };
    let seckey_bytes = match PrivateKeyBytes::from_hex_str(&private_key) {
        Ok(seckey_bytes) => seckey_bytes,
        Err(e) => {
            println!("Invalid private key({}), error({})", private_key, e);
            return;
        }
    };
    println!(
        "Adding keypair:\n\tpubkey: {}\n\tseckey: {}",
        pubkey_bytes, seckey_bytes
    );

    bc_manager
        .lock()
        .await
        .add_keypair(&pubkey_bytes, &seckey_bytes)
        .await
        .unwrap();
}

async fn handle_clear_utxos(bc_manager: &BcManagerHandler) {
    let mng = bc_manager.lock().await;
    mng.clear_utxos_in_wallet().await;
}

async fn handle_test_send_tx(bc_manager: &BcManagerHandler) {
    // let sender =
    //     PublicKeyHashBytes::from_hex_str(bc_base::constants::GENESIS_ACCOUNT_HASH).unwrap();
    let recipient =
        PublicKeyHashBytes::from_hex_str("c0b86b8c786e9dc809f1045fba1a099900721568").unwrap();
    let amount: TransactionValue = 100;
    let request = bc_manager::SendTransactionRequest::new(recipient, amount);
    let mng = bc_manager.lock().await;
    mng.send_transaction(request)
        .await
        .expect("Failed to send transaction");
}

fn print_help() {
    let cmd_list = "Available commands:\n\
         \texit, help, dump-acc, clear-utxos, test-send-tx,\n\
         \tadd-keypair <pubkey> <private_key>, recv-tx-trigger-mining <true/false>,\n\
         \tstart-mining-imm";

    println!("{}", cmd_list);
}
